//! gameboy register stuff

use crate::gbmode::GBMode;

#[derive(Copy, Clone)]
pub struct Register {
    pub a: u8,
    f: u8, // private
    pub b: u8,
    pub c: u8,

    pub d: u8,
    pub e: u8,
    pub h: u8,

    pub l: u8,
    pub pc: u16, // program count
    pub sp: u16, // stack pointer
}

#[derive(Copy, Clone)]
pub enum CpuFlag {
    C = 0b0001_0000,
    H = 0b0010_0000,
    N = 0b0100_0000,
    Z = 0b1000_0000,
}

impl Register {
    pub fn new(mode: GBMode) -> Self {
        match mode {
            GBMode::Classic => Self {
                a: 0x01,
                f: CpuFlag::C as u8 | CpuFlag::H as u8 | CpuFlag::Z as u8,
                b: 0x00,
                c: 0x13,

                d: 0x00,
                e: 0xD8,
                h: 0x01,
                l: 0x4D,

                pc: 0x0100,
                sp: 0xFFFE,
            },

            GBMode::Color => Self {
                a: 0x11,
                f: CpuFlag::Z as u8,
                b: 0x00,
                c: 0x00,

                d: 0xFF,
                e: 0x56,
                h: 0x00,
                l: 0x00,

                pc: 0x0100,
                sp: 0xFFFE,
            },

            GBMode::ColorAsClassic => Register {
                a: 0x11,
                f: CpuFlag::Z as u8,
                b: 0x00,
                c: 0x00,

                d: 0xFF,
                e: 0x56,
                h: 0x00,
                l: 0x00,

                pc: 0x0100,
                sp: 0xFFFE,
            },
        }
    }
    // getter
    pub fn af(&self) -> u16 {
        let tmp1 = (self.a as u16) << 8;
        let tmp2 = (self.f & 0xF0) as u16;

        tmp1 | tmp2
    }

    pub fn bc(&self) -> u16 {
        let tmp1 = (self.b as u16) << 8;
        let tmp2 = self.c as u16;

        tmp1 | tmp2
    }

    pub fn de(&self) -> u16 {
        let tmp1 = (self.d as u16) << 8;
        let tmp2 = self.l as u16;

        tmp1 | tmp2
    }

    pub fn hl(&self) -> u16 {
        let tmp1 = (self.h as u16) << 8;
        let tmp2 = self.l as u16;

        tmp1 | tmp2
    }

    pub fn hld(&mut self) -> u16 {
        let res = self.hl();

        self.sethl(res - 1);

        res
    }

    pub fn hli(&mut self) -> u16 {
        let res = self.hl();
        self.sethl(res + 1);

        res
    }

    pub fn getflag(&self, flags: CpuFlag) -> bool {
        let mask = flags as u8;
        self.f & mask > 0
    }

    // end getter

    // setter
    pub fn flag(&mut self, flags: CpuFlag, set: bool) {
        let mask = flags as u8;
        match set {
            true => self.f |= mask,
            false => self.f &= !mask,
        }
        self.f &= 0xF0;
    }

    pub fn setaf(&mut self, value: u16) {
        self.a = (value >> 8) as u8;
        self.f = (value & 0x00F0) as u8;
    }

    pub fn setbc(&mut self, value: u16) {
        self.b = (value >> 8) as u8;
        self.c = (value & 0x00FF) as u8;
    }

    pub fn setde(&mut self, value: u16) {
        self.d = (value >> 8) as u8;
        self.e = (value & 0x00FF) as u8;
    }

    pub fn sethl(&mut self, value: u16) {
        self.h = (value >> 8) as u8;
        self.l = (value & 0x00FF) as u8;
    }

    // end setter
}

// TODO: mod test
